package top.mcwebsite.lib.router.processor

import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.getClassDeclarationByName
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSValueParameter
import com.google.devtools.ksp.symbol.Nullability
import com.squareup.kotlinpoet.ksp.KotlinPoetKspPreview
import com.squareup.kotlinpoet.ksp.toTypeName
import top.mcwebsite.lib.router.annotation.Param
import top.mcwebsite.lib.router.annotation.Route
import top.mcwebsite.lib.router.processor.RouteNavGenerate.generate
import top.mcwebsite.lib.router.processor.utils.parcelableType
import top.mcwebsite.lib.router.processor.utils.parseKsType
import top.mcwebsite.lib.router.processor.utils.serializableType

class RouterSymbolProcessor(
    private val logger: KSPLogger,
    private val codeGenerator: CodeGenerator,
) : SymbolProcessor {

    private fun init(resolver: Resolver) {
        // TODO 类是否存在
        serializableType = resolver.getClassDeclarationByName("java.io.Serializable")!!.asType(emptyList())
        parcelableType = resolver.getClassDeclarationByName("android.os.Parcelable")!!.asType(emptyList())
    }

    override fun process(resolver: Resolver): List<KSAnnotated> {
        init(resolver)
        resolver.getSymbolsWithAnnotation(Route::class.qualifiedName!!)
            .filterIsInstance<KSFunctionDeclaration>()
            .map(::routeAnnotationProcess)
            .toList()
            .takeIf { it.isNotEmpty() }
            ?.generate(codeGenerator, logger)
        return emptyList()
    }

    @OptIn(KspExperimental::class)
    private fun routeAnnotationProcess(ksFun: KSFunctionDeclaration): RouteMeta {
        val routeAnnotation = ksFun.getAnnotationsByType(Route::class).first()
        val params = ksFun.parameters
            .asSequence()
            .map(::parseParamMeta)
            .filterNotNull()
            .onEach {
                logger.warn("route = $it")
            }
            .toList()
        logger.warn("ksFun simpleName = ${ksFun.simpleName.asString()}")
        logger.warn(
            "ksFun qualifiedName = ${
                ksFun.qualifiedName!!
                    .asString()
            }"
        )
        return RouteMeta(
            routePath = routeAnnotation.path,
            alternate = routeAnnotation.alternate.toList(),
            params = params,
            functionName = ksFun.qualifiedName!!.asString()
        )
    }

    @OptIn(KspExperimental::class, KotlinPoetKspPreview::class)
    private fun parseParamMeta(ksParameter: KSValueParameter): ParamMeta? {
        val funParamName = ksParameter.name!!.getShortName()
        logger.warn("param name = $funParamName")
        val paramAnnotation = ksParameter.getAnnotationsByType(Param::class).firstOrNull()
        return if (paramAnnotation == null) {
            if (ksParameter.hasDefault) {
                null
            } else {
                throw IllegalArgumentException("Compose router function parameter must have @Param annotation or has default value")
            }
        } else {
            ParamMeta(
                name = paramAnnotation.name,
                funParamName = funParamName,
                type = parseKsType(ksParameter.type.resolve()),
                nullable = ksParameter.type.resolve().nullability == Nullability.NULLABLE,
                typeName = ksParameter.type.resolve().toTypeName()
            )
        }
    }



}